package com.sarc.games.focus;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import javax.microedition.khronos.opengles.GL10;

import android.util.Log;

import com.sarc.games.GameAction;
import com.sarc.games.focus.ColorManager.ColorTheme;
import com.sarc.games.focus.ColorManager.ColorType;

public class Square 
{
	private final static String TAG = "SARC.GAME.FOCUS.Square";
	private float vertices[] = 
		{
			-.5f, .5f, 0f,
			-.5f, -.5f, 0f,
			.5f, -.5f, 0f,
			-.5f, .5f, 0f,
			.5f,-.5f, 0f,
			.5f, .5f, 0f
		};
		
	private FloatBuffer vertexBuffer = null;
	
	/*
	private float normR, normG, normB, normA;
	private float giveR, giveG, giveB, giveA;
	private float takeR, takeG, takeB, takeA;
	
	private float boundR, boundG, boundB, boundA;
	*/
	
	int currentLevel;
	
	private float[] squareColor;
	private float[] boundaryColor;
	
	private float boundSize, squareSize, locX, locY, rotation;

	double energyLevel;
	double decayLevel;
	double energyMax, energyMin;
	
	ArrayList<EnergyAction> actions;
	ArrayList<RotationAction> rotations;
	ArrayList<MovementAction> movements;
	long frameTime;
	
	public Square()
	{
		this(0,0);
	}
	
	public Square(float locX, float locY)
	{
		this(0, 0.005, 1f, -1f, locX, locY, 0.025f, 0.925f);
	}
	
	public Square(double energyLevel, double decayLevel, double energyMax, double energyMin, float locX, float locY, float boundSize, float squareSize)
	{

		setEnergyLevel(energyLevel);
		setDecayLevel(decayLevel);
		setMaxMinEnergyLevel(energyMax, energyMin);

		actions = new ArrayList<EnergyAction>();
		movements = new ArrayList<MovementAction>();
		rotations = new ArrayList<RotationAction>();
		
		ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
		vbb.order(ByteOrder.nativeOrder());
		vertexBuffer = vbb.asFloatBuffer();
		vertexBuffer.put(vertices);
		vertexBuffer.position(0);
		
		frameTime = 0;
		rotation = 0;
		this.locX = locX;
		this.locY = locY;
		this.boundSize = boundSize;
		this.squareSize = squareSize;
		currentLevel = 0;
	}
		
	public SquareStatus update(long newFrameTime, ColorManager colorManager)
	{
		
		//update energyLevel from each actions
		for (EnergyAction action : actions)
		{
			energyLevel += action.extractEnergyAt(newFrameTime);
		}
		GameAction.cleanGameAction(actions);
		
		//update location
		if (movements.size() > 0)
		{
			MovementAction movement = movements.get(0);
			movement.resolveMovement(newFrameTime);
			locX += movement.getDisplacementX();
			locY += movement.getDisplacementY();
			if (movement.shouldRemove())
			{
				movements.remove(0);
			}
		}
		
		//update rotation
		if (rotations.size() > 0)
		{
			RotationAction rotationAction = rotations.get(0);
			rotation += rotationAction.getRotation(newFrameTime);
			if (rotationAction.shouldRemove())
			{
				rotations.remove(0);
			}
		}
		
		Log.d(TAG, "ACTIONS"+
				": A(" + Integer.valueOf(actions.size()).toString() + 
				") M(" + Integer.valueOf(movements.size()).toString() +
				") R(" + Integer.valueOf(rotations.size()).toString() + ")");
		
		//normally it will dissipate energy itself
		energyLevel -= decayLevel;
		
		//update colors
		
		//giving Energy To Square
		/*
		if (energyLevel > 0)
		{
			float progress = (float) (energyLevel / energyMax); 
			
			currR = progress * giveR + (1-progress) * normR;
			currG = progress * giveG + (1-progress) * normG;
			currB = progress * giveB + (1-progress) * normB;
			currA = progress * giveA + (1-progress) * normA;
		}
		
		//taking energy from square
		else 
		{
			float progress = (float) (energyLevel / energyMin); 
			
			currR = progress * takeR + (1-progress) * normR;
			currG = progress * takeG + (1-progress) * normG;
			currB = progress * takeB + (1-progress) * normB;
			currA = progress * takeA + (1-progress) * normA;
		}
		*/
		
		ColorTheme colorTheme = colorManager.getCurrentColorTheme();
		float[] baseColor;
		float progress;
		if (energyLevel > 0)
		{
			baseColor = colorTheme.getBasicColorSet().getColorByType(ColorType.GIVE);
			progress = (float) (energyLevel / energyMax);
		}
		else
		{
			baseColor = colorTheme.getBasicColorSet().getColorByType(ColorType.TAKE);
			progress = (float) (energyLevel / energyMin);
		}
		squareColor = ColorManager.mixColor(colorTheme.getBasicColorSet().getColorByType(ColorType.NORMAL),
											baseColor,
											progress);
		boundaryColor = colorTheme.getBasicColorSet().getColorByType(ColorType.BOUNDARY);

		//benchmark time
		frameTime = newFrameTime;
		
		
		if (energyLevel < energyMin)
		{
			return SquareStatus.OVERTAKEN;
		}
		else if (energyLevel > energyMax)
		{
			return SquareStatus.OVERGIVEN;
		}
		else
		{
			return SquareStatus.NORMAL;
		}

	}
	
	public void render(GL10 gl)
	{
		//set up
		gl.glPushMatrix();
		gl.glTranslatef(locX, locY, 0);
		gl.glRotatef(rotation, 0f, 0f, 1f);
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
		
		//draw boundary
		gl.glPushMatrix();
		float boundSqSize = boundSize * 2 + squareSize;
		gl.glScalef(boundSqSize, boundSqSize, 1);
		gl.glTranslatef(0f,0f,0.001f); //translated to avoid z-buffer problem 
		gl.glColor4f(boundaryColor[0], boundaryColor[1], boundaryColor[2], boundaryColor[3]);
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
		gl.glPopMatrix();
		
		//draw the inner square
		gl.glScalef(squareSize, squareSize, 1f);
		gl.glColor4f(squareColor[0],squareColor[1],squareColor[2],squareColor[3]);
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
		
		//reset color
		gl.glColor4f(1, 1, 1, 1);
		
		//clean up
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisable(GL10.GL_CULL_FACE);
		gl.glPopMatrix();
	}
	
	/*
	public void setTakeColor(int r, int g, int b, int a)
	{
		this.takeR = ((float)r / 255.f);
		this.takeG = ((float)g / 255.f);
		this.takeB = ((float)b / 255.f);
		this.takeA = ((float)a / 255.f);
	}
	
	public void setGiveColor(int r, int g, int b, int a)
	{
		this.giveR = ((float)r / 255.f);
		this.giveG = ((float)g / 255.f);
		this.giveB = ((float)b / 255.f);
		this.giveA = ((float)a / 255.f);
	}
	
	public void setNormColor(int r, int g, int b, int a)
	{
		this.normR = ((float)r / 255.f);
		this.normG = ((float)g / 255.f);
		this.normB = ((float)b / 255.f);
		this.normA = ((float)a / 255.f);
	}
	
	public void setBoundaryColor(int r, int g, int b, int a)
	{
		this.boundR = ((float)r / 255.f);
		this.boundG = ((float)g / 255.f);
		this.boundB = ((float)b / 255.f);
		this.boundA = ((float)a / 255.f);
	}
	*/
	
	public void setGameLevel(int level)
	{
		this.currentLevel = level;
	}
	
	public void setDecayLevel(double decayLevel)
	{
		this.decayLevel = decayLevel;
	}
	
	public void setMaxMinEnergyLevel(double max, double min)
	{
		this.energyMax = max;
		this.energyMin = min;
	}
	
	public void setEnergyLevel(double energyLevel)
	{
		this.energyLevel = energyLevel;
	}
	
	public void takeEnergy(double energy, long time)
	{
		if (time > 0)
		{
			actions.add(new EnergyAction(-energy, time));
		}
		else
		{
			energyLevel -= energy;
		}
	}
	
	public void giveEnergy(double energy, long time)
	{
		if (time > 0)
		{
			actions.add(new EnergyAction(energy, time));
		}
		else
		{
			energyLevel += energy;
		}
		
	}
	
	public void clearActions()
	{
		actions.clear();
	}
	
	public void moveTo(float targetX, float targetY, long time)
	{
		if (time > 0)
		{
			movements.add(new MovementAction(targetX - locX, targetY - locY, time));
		}
		else
		{
			locX = targetX;
			locY = targetY;
		}
	}
	
	public void addMovement(float targetX, float targetY, long time)
	{
		if (time > 0)
		{
			movements.add(new MovementAction(targetX, targetY, time));
		}
		else
		{
			locX += targetX;
			locY += targetY;
		}
	}
	
	public void clearMovements()
	{
		movements.clear();
	}
	
	public void addRotation(float degree, long time)
	{
		if (time > 0)
		{
			rotations.add(new RotationAction(degree, time));
		}
		else
		{
			rotation += degree;
		}
	}
	
	public void clearRotations()
	{
		rotations.clear();
	}
	
	private class EnergyAction extends GameAction
	{
		private double maxEnergy;
		
		public EnergyAction(double energy, long timeAllowed)
		{
			super(timeAllowed);
			maxEnergy = energy;
		}
		
		public double extractEnergyAt(long frameTime)
		{
			resolve(frameTime);
			return deltaProgress() * maxEnergy;
		}
	}
	
	private class RotationAction extends GameAction
	{
		private double rotateTo;
		public RotationAction (double rotateTo, long timeAllowed)
		{
			super(timeAllowed);
			this.rotateTo = rotateTo;
		}
		public double getRotation(long frameTime)
		{
			resolve(frameTime);
			return deltaProgress() * rotateTo;
		}
	}
	
	private class MovementAction extends GameAction
	{
		private double targetX, targetY;
			
		public MovementAction(double deltaX, double deltaY, long timeAllowed)
		{
			super(timeAllowed);
			targetX = deltaX;
			targetY = deltaY;
		}
		
		public boolean resolveMovement(long frameTime)
		{
			return resolve(frameTime);
		}
				
		public double getDisplacementX()
		{
			return deltaProgress() * targetX;
		}
		
		public double getDisplacementY()
		{
			return deltaProgress() * targetY;
		}
	
	}
	
	public enum SquareStatus
	{
		NORMAL, OVERTAKEN, OVERGIVEN;
	}
}
